#include <bits/stdc++.h>
using namespace std;
#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
#include <numeric>
#include <functional>
#include <type_traits>

namespace scd
{
    using std::decay_t, std::enable_if_t, std::false_type, std::true_type;
    using std::declval, std::forward, std::move;
    using std::getchar, std::putchar;
    using std::is_floating_point, std::is_integral, std::is_reference, std::is_same;

    namespace trait
    {
        template <typename...>
        using void_t = void;

        template <typename, typename = void>
        struct has_read : std::false_type
        {
        };

        template <typename T>
        struct has_read<T, void_t<decltype(std::declval<T &>().read())>> : std::true_type
        {
        };

        template <typename T>
        inline constexpr bool has_read_v = has_read<T>::value;

        template <typename, typename = void>
        struct has_print : false_type
        {
        };

        template <typename T>
        struct has_print<T, void_t<decltype(declval<T>().print())>> : true_type
        {
        };

        template <typename T>
        inline constexpr bool has_print_v = has_print<T>::value;
    }

    using namespace trait;

    namespace io
    {
        template <typename Raw = int, typename T = decay_t<Raw>>
        inline auto read() -> enable_if_t<is_integral<T>::value, T>
        {
            T x = 0;
            bool fl = false;
            char c = getchar();
            while (c < '0' || c > '9')
                fl |= (c == '-');
            do
            {
                x = (x << 3) + (x << 1) + (c ^ 48);
                c = getchar();
            } while (c >= '0' && c <= '9');
            return fl ? -x : x;
        }

        template <typename Raw, typename T = decay_t<Raw>>
        inline auto read() -> enable_if_t<!is_integral<T>::value, T>
        {
            T x;
            std::cin >> x;
            return x;
        }

        template <typename Raw, typename T = decay_t<Raw>>
        inline auto read(Raw &&x) -> enable_if_t<is_integral<T>::value>
        {
            x = 0;
            bool fl = false;
            char c = _getchar_nolock();
            while (c < '0' || c > '9')
                fl |= (c == '-');
            do
            {
                x = (x << 3) + (x << 1) + (c ^ 48);
                c = _getchar_nolock();
            } while (c >= '0' && c <= '9');
        }

        template <typename T>
        inline auto read(T &&x) -> enable_if_t<has_read_v<T>>
        {
            x.read();
        }

        template <typename Raw = int, typename T = decay_t<Raw>>
        inline auto print(Raw x) -> enable_if_t<is_integral<T>::value>
        {
            if (x < 0)
            {
                putchar('-');
                x = -x;
            }
            if (x > 9)
                print(x / 10);
            putchar(x % 10 + '0');
        }

        template <int d = 6, typename Raw = double, typename T = decay_t<Raw>>
        inline auto print(Raw x) -> enable_if_t<is_floating_point<T>::value>
        {
            std::printf("%.*f", d, x);
        }

        inline auto print(char x)
        {
            putchar(x);
        }

        inline void print(const char *x)
        {
            while (*x)
            {
                putchar(*x);
                ++x;
            }
        }

        template <typename T>
        inline auto print(T &&x) -> enable_if_t<has_print_v<T>>
        {
            x.print();
        }

        template <typename Raw, typename T = decay_t<Raw>>
        inline auto print(Raw &&x)
            -> enable_if_t<!is_integral<T>::value &&
                           !is_floating_point<T>::value &&
                           !has_print<Raw>::value>
        {
            std::cout << x;
        }

        template <typename... T>
        inline void print(T &&...x)
        {
            (print(forward<T>(x)), ...);
        }

        template <typename T>
        inline void printws(T &&x)
        {
            print(x, ' ');
        }

        template <typename T>
        inline void println(T &&x)
        {
            print(x, '\n');
        }
    }

    template <typename T = int>
    class Vec
    {
        std::vector<T> v;

    public:
        Vec() noexcept = default;

        Vec(const Vec &V) noexcept = default;

        Vec(Vec &&V) noexcept = default;

        Vec(size_t n) noexcept : v(n) {}

        Vec(size_t n, const T &val) noexcept : v(n, val) {}

        Vec(std::initializer_list<T> l) noexcept : v(l) {}

        Vec(const std::vector<T> &V) noexcept : v(V) {}

        Vec(std::vector<T> &&V) noexcept : v(move(V)) {}

        template <typename I>
        Vec(I fir, I lst) noexcept : v(fir, lst) {}

        Vec(const std::string &s) noexcept : v(s.begin(), s.end()) {}

        Vec(std::string &&s) noexcept : v(std::make_move_iterator(s.begin()),
                                          std::make_move_iterator(s.end())) {}

        Vec(const char *s) noexcept : v(s, s + std::strlen(s)) {}

        auto begin() const noexcept
        {
            return v.begin();
        }

        auto end() const noexcept
        {
            return v.end();
        }

        auto &operator[](int i) noexcept
        {
            return v[i];
        }

        template <typename... Args>
        auto emplace_back(Args &&...args) noexcept
        {
            return v.emplace_back(std::forward<Args>(args)...);
        }

        template <typename U>
        auto push_back(U &&x) noexcept
        {
            return v.push_back(std::forward<U>(x));
        }

        void pop_back() noexcept
        {
            v.pop_back();
        }

        auto size() const noexcept
        {
            return v.size();
        }

        void reserve(size_t n) noexcept
        {
            v.reserve(n);
        }

        void read() noexcept
        {
            for (auto &item : v)
                io::read(item);
        }

        template <bool no_white_space = false>
        void print() const noexcept
        {
            if constexpr (no_white_space)
                for (const auto &item : v)
                    io::print(item);
            else
                for (const auto &item : v)
                    io::print(item, ' ');
        }
    };

    using Str = Vec<char>;

    // todo! IDIOM copy and swap
    class CowStr
    {
    private:
        Str *s;
        unsigned *cnt;

    public:
        CowStr() noexcept : s(new Str()), cnt(new unsigned(1)) {}

        CowStr(const CowStr &c) noexcept
        {
            s = c.s;
            cnt = c.cnt;
            ++*cnt;
        }

        CowStr(CowStr &&c) noexcept = default;

        CowStr(const Str &s) noexcept : s(new Str(s)), cnt(new unsigned(1)) {}

        CowStr(Str &&s) noexcept : s(new Str(move(s))), cnt(new unsigned(1)) {}

        CowStr(const std::string &s) noexcept : s(new Str(s)), cnt(new unsigned(1)) {}

        CowStr(std::string &&s) noexcept : s(new Str(move(s))), cnt(new unsigned(1)) {}

        CowStr(const char *s) noexcept : s(new Str(s)), cnt(new unsigned(1)) {}

        CowStr &operator=(const CowStr &c) noexcept
        {
            if (this != &c)
            {
                if (--*cnt == 0)
                {
                    delete s;
                    delete cnt;
                }
                s = c.s;
                cnt = c.cnt;
                ++*cnt;
            }
            return *this;
        }

        CowStr &operator=(CowStr &&c) noexcept = default;

        CowStr &operator=(const Str &str) noexcept
        {
            if (--*cnt == 0)
            {
                delete s;
                delete cnt;
            }
            s = new Str(str);
            cnt = new unsigned(1);
            return *this;
        }

        CowStr &operator=(Str &&str) noexcept
        {
            if (--*cnt == 0)
            {
                delete s;
                delete cnt;
            }
            s = new Str(move(str));
            cnt = new unsigned(1);
            return *this;
        }

        CowStr &operator=(const std::string &str) noexcept
        {
            if (--*cnt == 0)
            {
                delete s;
                delete cnt;
            }
            s = new Str(str);
            cnt = new unsigned(1);
            return *this;
        }

        CowStr &operator=(std::string &&str) noexcept
        {
            if (--*cnt == 0)
            {
                delete s;
                delete cnt;
            }
            s = new Str(move(str));
            cnt = new unsigned(1);
            return *this;
        }

        CowStr &operator=(const char *str) noexcept
        {
            if (--*cnt == 0)
            {
                delete s;
                delete cnt;
            }
            s = new Str(str);
            cnt = new unsigned(1);
            return *this;
        }

        ~CowStr() noexcept
        {
            if (--*cnt == 0)
            {
                delete s;
                delete cnt;
            }
        }

        auto &operator[](int i) noexcept
        {
            return (*s)[i];
        }

        void read() noexcept
        {
            s->read();
        }

        void print() const noexcept
        {
            s->print<true>();
        }
    };

    namespace exports
    {
        using io::print, io::println, io::printws, io::read;
        using scd::Vec, scd::Str, scd::CowStr;
    }
}
using namespace scd::exports;
using ll = int64_t;
constexpr int N = 3005, A = 26;
int n, m;
char a[N][N];
int rt[N][N];
int main()
{
    // freopen("ex_star4.in", "r", stdin);
    freopen("star.in", "r", stdin);
    freopen("star.out", "w", stdout);
    read(n), read(m);
    for (int i = 0; i < n; ++i)
    {
        // char c = _getchar_nolock();
        // while (c < 'a' || c > 'z')
        //     c = _getchar_nolock();
        // for (int j = 0; j < m; ++j)
        //     a[i][j] = _getchar_nolock();
        scanf("%s", a[i]);
        rt[i][m - 1] = 1;
        a[i][m - 1] -= 'a';
        for (int j = m - 2; j >= 0; --j)
        {
            a[i][j] -= 'a';
            rt[i][j] = (a[i][j] == a[i][j + 1]) * rt[i][j + 1] + 1;
        }
    }
    ll res = 0;
    for (int l = 0; l < m; ++l)
    {
        for (int r = l + 1; r < m; ++r)
            for (int c = 0; c < A; ++c)
            {
                int k = 0;
                bool fl = 0;
                for (int i = 0; i < n; ++i)
                {
                    if (a[i][l] == c && a[i][r] == c)
                    {
                        fl = 1;
                        if (a[i][l] == c && rt[i][l] >= r - l + 1)
                            ++k;
                    }
                    else if (fl)
                    {
                        res += (k >= 2) * 1LL * k * (k - 1) / 2;
                        k = 0;
                        fl = 0;
                    }
                }
                res += (fl && k >= 2) * 1LL * k * (k - 1) / 2;
            }
#ifdef _DEBUG
        if (l % 100 == 0)
            cerr << "TIME: " << l << " " << 1.0 * clock() / CLOCKS_PER_SEC << endl;
#endif
    }
    println(res);
    return 0;
}
